package sucursal;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;



import java.util.Map;

import ofertas.Oferta;



import ofertas.OfertaCompuesta;
import ofertas.OfertaProductos;
import ofertas.OfertaUnSoloProducto;





//import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import clientes.Cliente;
//import clientes.Ticket;
import pedidos.Pedido;
import productosYPresentaciones.Producto;
import productosYPresentaciones.Ubicacion;
//import reporte.Reporte;
import sucursal.Sucursal;
import ventas.Venta;
//simport ventas.VentaDirecta;
import ventas.VentaDirectaConCliente;
import cadenaDeSucursales.CadenaSucursales;
import exceptions.ClienteNoEncontradoException;
import exceptions.StatesException;
import exceptions.VentasException;

/**
 * Clase test de Sucursal
 * @author Stefania
 * @author Octavio (tests de aplicar ofertas)
 *
 */

public class SucursalTest {
	
Sucursal s;
CadenaSucursales cadena;
Producto productoMock1;
Producto productoMock2;
Venta venta;
Pedido pedidoMock;

@Before
public void setUp() throws Exception {
	//this.re= mock(Reporte.class);
	this.cadena= mock(CadenaSucursales.class);
	this.s= new Sucursal("Quilmes", cadena);
	
	this.productoMock1= mock(Producto.class);
	this.productoMock2= mock(Producto.class);
	this.pedidoMock= mock(Pedido.class);
	List<Producto>productos= new ArrayList<Producto>();
	productos.add(productoMock1);
	productos.add(productoMock2);
	when(pedidoMock.getProductosDelPedido()).thenReturn(productos);
	
	s.agregarProductoDelStock(productoMock1, 5);
	
	this.venta= mock(VentaDirectaConCliente.class);
	
	
}
	
	
	@Test
	public void testGetNombre(){
		assertEquals("Quilmes",s.getNombre());
	}
	
	@Test
	public void realizarCompraTestJuntoAgetVentasRealizadasTest() throws VentasException, StatesException{
		s.realizarCompra(venta);
		assertTrue(this.s.getVentasRealizadas().contains(venta));
		verify(venta,times(1)).realizarCompra();
	}
	
	@Test
	/***
     *  
	 */
	public void testGetCantidadStockProducto(){
	
		s.getCantStockProducto(productoMock1);
		
		assertEquals(5,s.getCantStockProducto(productoMock1));
		
	}
	
	@Test
	/**
	 * Test del m�todo "removerProductoDelStock(Producto, int)"
	 * Se tuvo que agregar primero objetos dummies a la lista 
	 * para poder testear la funcionalidad del m�todo.
	 */
	public void testRemoverProductoDelStock(){

		s.removerProductoDelStock(productoMock1,2);
		
		assertEquals(3,s.getCantStockProducto(productoMock1));
		
	}
	
	
	@Test 
	public void testTraerProductoASucursal(){
		
		Sucursal sucursal2= new Sucursal("Bernal", cadena);
		sucursal2.agregarProductoDelStock(productoMock1, 15);
		
		s.traerProducto(productoMock1,3,sucursal2);
		
		assertEquals(8,s.getCantStockProducto(productoMock1));
		assertEquals(12,sucursal2.getCantStockProducto(productoMock1));		
	}
	
	@Test
	public void testPedirProductoASucursal(){
		Producto unidad= mock(Producto.class);
		
		Stock stockM= new Stock();
		stockM.agregarStock(unidad, 2);
		
		Sucursal mock= mock(Sucursal.class);
		
		List<Sucursal>sucursalesT= new ArrayList<Sucursal>();
		when(cadena.getSucursales()).thenReturn(sucursalesT);
		
		s.pedirProducto(unidad,2);
		
		verify(cadena,times(1)).pedirProducto(unidad,2, s);
		
	}
	
///
	@Test 
	/**
	 * 
	 */
	public void testAgregarACCPesos() throws ClienteNoEncontradoException {
		
		Cliente mock= mock(Cliente.class);
	    String dni= "36664421";
		when(mock.getCcPesos()).thenReturn(0f);
		when(mock.getDni()).thenReturn("36664421");
		when(cadena.buscarCliente(dni)).thenReturn(mock);
		
			s.agregarACCPesos("36664421",50f);
			verify(mock,times(1)).agregarACuentaCorrientePesos(50f);
		
	}
	

	@Test
	public void testAgregarACCProductos() throws ClienteNoEncontradoException {
		Cliente mock= mock(Cliente.class);
		Stock p= new Stock();
		Producto prod= mock(Producto.class);
		
		String dni= "36664421";
		
		when(mock.getCcProductos()).thenReturn(p);
		when(mock.getDni()).thenReturn(dni);
		when(cadena.buscarCliente(dni)).thenReturn(mock);
			s.agregarACCProductos("36664421",prod,8);	
		
		verify(mock, times(1)).agregarACuentaCorrienteProductos(prod,8);
		
	}
	
	
	/**
	 * 
	 */
     @Test
     public void agregarProductoDelStock(){
    	 
    	 s.agregarProductoDelStock(productoMock1,5);
    	 
    	 assertEquals(10,s.getCantStockProducto(productoMock1));
    	 
    	 
     }
     
     
     //hacer caso true y false.
     /**
      * 
      */
     @Test
     public void testHayStockEnEstaSucursal(){ //true
    	 s.agregarProductoDelStock(productoMock1, 10);
    	
    	 assertTrue(s.hayStockEnEstaSucursal(productoMock1, 5));
     }
     
     /**
      * 
      */
     @Test
     public void testHayStockEnEstaSucursalFalse(){ //false
    	 
    	 Producto noExiste= mock(Producto.class);
    	 
          assertFalse(s.hayStockEnEstaSucursal(noExiste,3));
    	 
    	 
     }

     @Test
     
     public void testHayStockEnAlgunaSucursalTrue(){
    	 Sucursal una= mock(Sucursal.class);
    	 Sucursal otra= mock(Sucursal.class);
    	 
    	 Stock stockUna= new Stock();
    	 Stock stockOtra= new Stock();
    	 
    	 Producto mock= mock(Producto.class);
    	 stockOtra.agregarStock(mock, 6);
    	 
    	 when(una.getStockProductos()).thenReturn(stockUna);
    	 when(otra.getStockProductos()).thenReturn(stockOtra);
    	 
    	 s.hayStockEnAlgunaSucursal(mock,3);
    	 
    	 verify(cadena,times(1)).hayStockEnAlgunaSucursal(mock,3);
    	
    	 
     }
     
     @Test
     public void testHayStockEnAlgunaSucursalFalse(){
    	 Sucursal una= mock(Sucursal.class);
    	 Sucursal otra= mock(Sucursal.class);
    	 
    	 Stock stockUna= new Stock();
    	 Stock stockOtra= new Stock();
    	 
    	 Producto mock= mock(Producto.class);
    	 
    	 when(una.getStockProductos()).thenReturn(stockUna);
    	 when(otra.getStockProductos()).thenReturn(stockOtra);
    	 
    	 s.hayStockEnAlgunaSucursal(mock,3);
    	 
    	 
    	 verify(cadena,times(1)).hayStockEnAlgunaSucursal(mock,3);
     }

     
     
   ///Tests de la parte extra!!

     @Test
     public void registrarPedidoTestSiNoExiste(){
    	 
    	 
    	 this.s.registrarPedido(pedidoMock);
    	 assertEquals(2,s.getPedidos().values().size());
    
     }
     

     @Test
     public void registrarPedidoTestSiExiste(){
    List<Producto>productos= new ArrayList<Producto>();
    productos.add(productoMock1);
    productos.add(productoMock2);
    when(pedidoMock.getProductosDelPedido()).thenReturn(productos);
    Pedido pedidoMock2=mock(Pedido.class);
    when(pedidoMock2.getProductosDelPedido()).thenReturn(productos);
    	 this.s.registrarPedido(pedidoMock);
    	 this.s.registrarPedido(pedidoMock2);
    	 assertEquals(2,s.getPedidos().values().size());
     }
     
     
     @Test
     public void avisarAInteresadosTestCuandoElMapDePedidosTieneAlProducto(){
    	 Cliente mockClienteInteresado= mock(Cliente.class);
    	 when(pedidoMock.getElQueLoHizo()).thenReturn(mockClienteInteresado);
    	 s.avisarAInteresados(productoMock1);
    // es un this de sucursal u.u
     }
     
     

     @Test
     public void puedeCubrirTestTrue(){
    	 
    	 Stock loPedido= new Stock();
    	 loPedido.agregarStock(productoMock1, 2);
    	 
    	 assertTrue(s.puedeCubrir(loPedido));
     }
     
     @Test
     public void puedeCubrirTestFalse(){
    	 
    	 Stock loPedido= new Stock();
    	 loPedido.agregarStock(productoMock1, 6);
    
    	 assertFalse(s.puedeCubrir(loPedido));
     }
     
     @Test
     public void notificarAlClienteTest(){
    	 
    	 Cliente clienteMock= mock(Cliente.class);
    	 when(pedidoMock.getElQueLoHizo()).thenReturn(clienteMock);
    	 s.notificarAlCliente(pedidoMock);
    	 
    	 verify(pedidoMock.getElQueLoHizo(),times(1)).notificarPedidoCompleto(pedidoMock);
     }
     
     
     ///////////
     
     @Test
     public void testAgregarProductoAUbicacion(){
    	 List<Ubicacion>ubicacionesProducto= new ArrayList<Ubicacion>();
  
    	 s.agregarProductosAUbicacion(productoMock1, ubicacionesProducto);
    	 
    	 assertTrue(s.getUbicacionEnLaSucursal().containsKey(productoMock1));
     }

     //Test de aplicarOferta,
     
   
     
     
     @Test
     public void testAplicarOfertaDeUnSoloProducto(){
    	 Map<Producto,Integer> productos = new HashMap<Producto,Integer>();
    	 Producto p1 = mock( Producto.class ),
    			 p2 = mock( Producto.class );
    	 //El changuito que va a pasar(el map)
    	 productos.put(p1, 3);
    	 productos.put(p2, 2);
    	 //Producto que estara asociado a la oferta1
    	 Map<Producto,Integer> lista1 = new HashMap<Producto,Integer>();
    	 lista1.put(p1,1);
    	//Producto que estara asociado a la oferta2
    	 Map<Producto,Integer> lista2 = new HashMap<Producto,Integer>();
    	 lista2.put(p2,1);
    	 
    	 List<Oferta> listaOfertas = new ArrayList<Oferta>();
    	 
    	// Oferta que se aplicara 3 veces, con un costo de 5$ cada uno
    	 Oferta oferta1 = mock( OfertaUnSoloProducto.class );
    	 when( oferta1.getProductos()).thenReturn(lista1);
    	 when( oferta1.precioOferta() ).thenReturn( 5f );
    	 //when( oferta1.puedeAplicarOferta( (Map<Producto,Integer>) any() )).thenCallRealMethod();
    	 	//.thenReturn(true, true, true,false);
    	 doCallRealMethod().when(oferta1).puedeAplicarOferta((Map<Producto,Integer>) any());
    	 // Oferta que se aplicara 2 veces, con un costo de 10$ cada uno
    	 Oferta oferta2 = mock( OfertaUnSoloProducto.class );
    	 when( oferta2.getProductos()).thenReturn(lista2);
    	 when( oferta2.precioOferta() ).thenReturn( 10f );
    	 //when( oferta2.puedeAplicarOferta( (Map<Producto,Integer>) any() ));
    	 	//.thenReturn(true, true,false);
    	 
    	 doCallRealMethod().when(oferta2).puedeAplicarOferta((Map<Producto,Integer>) any());
    	 
    	 
    	 listaOfertas.add(oferta1);
    	 listaOfertas.add(oferta2);
    	 
    	 when(cadena.getOfertasVigentes()).thenReturn(listaOfertas);
    	 
    	 assertEquals( 3*5f + 2*10f, s.aplicarOfertasConDescuento(productos), 0.002);
     	 //Nota: Como utiliza una copia del map, no se puede verificar si se usaron todas los productos
    	 //hay que usar la implementacion real, sin mocking
     }
     
     @Test
     public void testAplicarOfertaProductos(){
    	 Map<Producto,Integer> productos = new HashMap<Producto,Integer>();
    	 Producto p1 = mock( Producto.class ),
    			p2 = mock( Producto.class ),
    			p3 = mock( Producto.class ),
    			p4 = mock( Producto.class );
    	 //El changuito que va a pasar(el map)
    	 productos.put(p1, 2);
    	 productos.put(p2, 5);
    	 productos.put(p3, 2);
    	 productos.put(p4, 4);
    	 
    	 // Changuito(2,5,1,4) = (0,0,1,0) + 2*Oferta1(1,2,0,1) + 1*Oferta2(0,1,1,2)
    	 
    	 //Productos que estaran asociados a la oferta1
    	 Map<Producto,Integer> lista1 = new HashMap<Producto,Integer>();
    	 lista1.put(p1,1);
    	 lista1.put(p2,2);
    	 lista1.put(p4,1);
    	 
    	 //Productos que estaran asociados a la oferta2
    	 Map<Producto,Integer> lista2 = new HashMap<Producto,Integer>();
    	 lista2.put(p2,1);
    	 lista2.put(p3,1);
    	 lista2.put(p4,2);
    	 
    	 List<Oferta> listaOfertas = new ArrayList<Oferta>();
    	 
    	// Oferta que se aplicara 3 veces, con un costo de 5$ cada uno
    	 Oferta oferta1 = mock( OfertaProductos.class );
    	 when( oferta1.getProductos()).thenReturn(lista1);
    	 when( oferta1.precioOferta() ).thenReturn( 5f );
    	 doCallRealMethod().when(oferta1).puedeAplicarOferta((Map<Producto,Integer>) any());
    	 //when( oferta1.puedeAplicarOferta( (Map<Producto,Integer>) any() )).thenReturn(true, true, false);
    	 
    	 // Oferta que se aplicara 2 veces, con un costo de 10$ cada uno
    	 Oferta oferta2 = mock( OfertaProductos.class );
    	 when( oferta2.getProductos()).thenReturn(lista2);
    	 when( oferta2.precioOferta() ).thenReturn( 10f );
    	 doCallRealMethod().when(oferta2).puedeAplicarOferta((Map<Producto,Integer>) any());
    	 //when( oferta2.puedeAplicarOferta( (Map<Producto,Integer>) any() )).thenReturn(true, false);
    	 
    	 listaOfertas.add(oferta1);
    	 listaOfertas.add(oferta2);
    	 
    	 when(cadena.getOfertasVigentes()).thenReturn(listaOfertas);
    	 
    	 assertEquals( 2*5f + 10f, s.aplicarOfertasConDescuento(productos), 0.002);
     }
     
     @Test
     public void testAplicarOfertaCompuesta(){

    	 Map<Producto,Integer> productos = new HashMap<Producto,Integer>();
    	 Producto p1 = mock( Producto.class ),
    			p2 = mock( Producto.class ),
    			p3 = mock( Producto.class ),
    			p4 = mock( Producto.class );
    	 //El changuito que va a pasar(el map)
    	 productos.put(p1, 7);
    	 productos.put(p2, 5);
    	 productos.put(p3, 5);
    	 productos.put(p4, 10);
    	 
    	 //ofertadeunsoloproducto(1,0,0,0)
    	 //ofertadeproductos(0,1,1,2)
    	 //ofertacompuesta1(2,1,1,2) = 2*ofertadeunsoloproducto(1,0,0,0) + ofertadeproductos(0,1,1,2)
    	 //ofertacompuesta2(2,2,2,4) = ofertacompuesta1(2,1,1,2) + ofertadeproductos(0,1,1,2)
    	 //Changuito(6,5,5,10) = ofertacompuesta1(2,1,1,2) + 2*ofertacompuesta2(2,2,2,4)
    	 //Sobrara un producto p1
    	 
    	 //Productos que estaran asociados a la oferta de un solo producto
    	 List<Producto> listadeunsoloproducto = new ArrayList<Producto>();
    	 listadeunsoloproducto.add(p1);
    	 Oferta ofertadeunsoloproducto = mock( OfertaUnSoloProducto.class );
    	 when( ofertadeunsoloproducto.getListaProductos()).thenReturn(listadeunsoloproducto);
    	 when( ofertadeunsoloproducto.precioOferta() ).thenReturn( 40f );

    	 //Productos que estaran asociados a la oferta de varios productos
    	 List<Producto> listadeproductos = new ArrayList<Producto>();
    	 listadeproductos.add(p2);
    	 listadeproductos.add(p3);
    	 listadeproductos.add(p4);
    	 listadeproductos.add(p4);
    	 Oferta ofertadeproductos = mock( OfertaProductos.class );
    	 when( ofertadeproductos.getListaProductos()).thenReturn(listadeproductos);
    	 when( ofertadeproductos.precioOferta() ).thenReturn( 20f );
    	 doCallRealMethod().when(ofertadeproductos).puedeAplicarOferta((Map<Producto,Integer>) any());
    	 
    	 //Creo las listas de ofertas de la sucursal y de las ofertas
    	 List<Oferta> listaOfertas = new ArrayList<Oferta>(),
    			 listaOfertaComp1 = new ArrayList<Oferta>(),
    			 listaOfertaComp2 = new ArrayList<Oferta>();
    	 
    	 listaOfertaComp1.add(ofertadeunsoloproducto);
    	 listaOfertaComp1.add(ofertadeunsoloproducto);
    	 listaOfertaComp1.add(ofertadeproductos);
    	 
    	// OfertaCompuesta con 2 oferta simple y multiple, ejecutada 1 vez
    	 Oferta ofertacompuesta1 = new OfertaCompuesta( 0.5f, listaOfertaComp1 );
    	 listaOfertaComp2.add(ofertadeproductos);
    	 listaOfertaComp2.add(ofertacompuesta1);
    	 
    	 // OfertaCompuesta con 1 ofertaCompuesta y una multiple, ejecutada 2 veces
    	 Oferta ofertacompuesta2 = new OfertaCompuesta( 0.5f,listaOfertaComp2);

    	 //El orden si es importante, determinara que termine correctamente el test.
    	 listaOfertas.add(ofertacompuesta2);
    	 listaOfertas.add(ofertacompuesta1);
    	 
    	 when(cadena.getOfertasVigentes()).thenReturn(listaOfertas);
    	 
    	 assertEquals( 2*(((2*40f+20f)/2)+20f)/2 + (2*40f+20f)/2, s.aplicarOfertasConDescuento(productos), 0.002);
     }
     
     
     
     
     
     ///Parte recien agregada...
     ///Porque tambien hacia falta los metodos para agregar stocks al stock.
     
     @Test
     public void testAgregarStockAlStockDeLaSucursal(){
    	 Stock nuevo= new Stock();
    	 nuevo.agregarStock(productoMock1, 15);
    	 s.agregarStockAlStockDeSucursal(nuevo);
    	 
    	 assertEquals(20, s.getCantStockProducto(productoMock1));
     }
     
     @Test
     public void testRemoverStockAlStockDeLaSucursal(){
    	 Stock nuevo= new Stock();
    	 nuevo.agregarStock(productoMock1, 2);
    	 s.removerStockDelStockDeSucursal(nuevo);
    	 
    	 assertEquals(3, s.getCantStockProducto(productoMock1));
     }
      
     
}